package com.ruoyi.common.oss.service;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.ruoyi.common.exception.BizException;
import com.ruoyi.common.oss.config.AliyunOssProperties;
import com.ruoyi.common.oss.constant.Constants;
import com.ruoyi.common.utils.ConvertUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 阿里云对象储存工具类
 *
 * @author z越
 * @date 2024/2/19
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AliyunOssService {

    private final AliyunOssProperties aliyunOssProperties;
    private final OSS ossClient;
    private static final String SIMPLE_DATE_FORMAT = "yyyyMMdd";

    /**
     * 上传附件到阿里 OSS 中
     *
     * @param inputStream    上传的文件流
     * @param extensionName  文件的扩展名
     * @param attachmentId   附件的全局惟一序列
     * @return 保存成功的文件相对路径
     */
    public String upload(InputStream inputStream, String extensionName, long attachmentId) {
        // 生成有目录格式的 OSS 文件名
        String relativePath = generateUrlPath(extensionName, attachmentId);
        String bucketName = aliyunOssProperties.getBucketName();
        return upload(bucketName, inputStream, relativePath);
    }

    /**
     * 生成处理后的文件路径
     * generateUrlPath("user","jpg", 12345) => /user/20170320/12345.jpg
     *
     * @param extName        文件扩展名, 如: jpg, png, exe, ...
     * @param attachmentId   附件ID, 全系统中唯一的附件序列
     * @return 生成的文件路径
     */
    public static String generateUrlPath(String extName, long attachmentId) {
        if (StringUtils.isBlank(extName)) {
            throw new BizException("附件类型或附件扩展名为空！");
        }
        extName = ('.' == extName.charAt(0)) ? extName.substring(1, extName.length()) : extName;
        return "/TFNCGarden/" +
                ConvertUtils.toString(new Date(), SIMPLE_DATE_FORMAT) + "/" +
                attachmentId + "." + extName.trim().toLowerCase();
    }

    private String upload(String bucketName, InputStream inputStream, String relativePath) {
        if (null == inputStream) {
            throw new BizException("上传的文件流为空！");
        }
        try {
            // 先检查 Bucket, 如果不存在指定 Bucket 就创建
            bucketName = createBucketIfNeeded(bucketName);
            // 生成有目录格式的 OSS 文件名
            ossClient.putObject(bucketName, getOssKey(relativePath), inputStream);
            return relativePath;
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    /**
     * OSS Key 不能以 "/" 开头
     *
     * @param filePath 文件全路径，如: /user/20170320/12345.jpg
     * @return 去掉开头的 "/"
     */
    private static String getOssKey(String filePath) {
        return ('/' == filePath.charAt(0)) ? filePath.substring(1) : filePath;
    }

    /**
     * 先检查 Bucket, 如果不存在指定 Bucket 就创建
     *
     * @param bucketName 正常的 bucket 名字
     */
    private  String createBucketIfNeeded(String bucketName) {
        if (StringUtils.isBlank(bucketName) || StringUtils.equalsIgnoreCase(aliyunOssProperties.getBucketName(), bucketName.trim())) {
            return aliyunOssProperties.getBucketName();
        }

        try {
            bucketName = bucketName.trim();
            if (ossClient.doesBucketExist(bucketName)) {
                return bucketName;
            } else {
                CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
                createBucketRequest.setStorageClass(StorageClass.Standard);
                ossClient.createBucket(createBucketRequest);
                return bucketName;
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    /**
     * 分片上传
     *
     * @param file 文件
     * @return
     */
    public String multipartUpload(File file) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(aliyunOssProperties.getEndpoint(), aliyunOssProperties.getAccessKeyId(), aliyunOssProperties.getAccessKeySecret());
        // 上传路径
        String key = Constants.AliYun.OSS_FILE_PREFIX + file.getName();
        try {
            // 创建InitiateMultipartUploadRequest对象。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(aliyunOssProperties.getBucketName(), key);

            // 初始化分片。
            InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
            // 返回uploadId，它是分片上传事件的唯一标识，您可以根据这个uploadId发起相关的操作，如取消分片上传、查询分片上传等。
            String uploadId = upresult.getUploadId();

            // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
            List<PartETag> partETags = new ArrayList<>();
            // 计算文件有多少个分片。
            final long partSize = 1 * 1024 * 1024L;
            long fileLength = file.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
            // 遍历分片上传。
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                InputStream instream = new BufferedInputStream(new FileInputStream(file));
                // 跳过已经上传的分片。
                instream.skip(startPos);
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(aliyunOssProperties.getBucketName());
                uploadPartRequest.setKey(key);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100 KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
                uploadPartRequest.setPartNumber(i + 1);
                // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后，OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
                partETags.add(uploadPartResult.getPartETag());
            }
            /*
                创建CompleteMultipartUploadRequest对象。
                 在执行完成分片上传操作时，需要提供所有有效的partETags。
                 OSS收到提交的partETags后，会逐一验证每个分片的有效性。
                 当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
            */
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(aliyunOssProperties.getBucketName(), key, uploadId, partETags);
            // 完成上传。
            CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            // 返回文件存放路径
            return completeMultipartUploadResult.getLocation();
        } catch (Exception e) {
            log.error("oss multipartUpload error:{}", e);
        } finally {
            // 关闭OSSClient。
            ossClient.shutdown();
        }
        return null;
    }

    /**
     * 删除单个文件
     *
     * @param fileName 文件名
     */
    public void removeFile(String fileName) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(aliyunOssProperties.getEndpoint(), aliyunOssProperties.getAccessKeyId(), aliyunOssProperties.getAccessKeySecret());
        try {
            //删除文件
            ossClient.deleteObject(aliyunOssProperties.getBucketName(), Constants.AliYun.OSS_FILE_PREFIX + fileName);
        } catch (Exception e) {
            log.error("oss delete file error:[}", e);
        } finally {
            ossClient.shutdown();

        }
    }

    public List<String> getFileList(String prefix) {
        List<String> urlList = new ArrayList<>();

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(aliyunOssProperties.getEndpoint(), aliyunOssProperties.getAccessKeyId(), aliyunOssProperties.getAccessKeySecret());
        try {
            // 列举指定bucket下的文件
            ObjectListing objectListing = ossClient.listObjects(aliyunOssProperties.getBucketName() , prefix);

            String folderName = prefix.endsWith("/") ? prefix : prefix + "/";

            // 遍历所有文件
            for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {

                if(Objects.equals(folderName,objectSummary.getKey())){
                    continue;
                }

                //得到key
                urlList.add("https://" + aliyunOssProperties.getEndpoint() + "/" + objectSummary.getKey());
            }

        } catch (Exception e) {
            log.error("oss delete file error:[}", e);
        } finally {
            ossClient.shutdown();
            return urlList;
        }
    }


}
